package org.bjf.modules.core.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.bjf.exception.CommMsgCode;
import org.bjf.exception.ServiceException;
import org.bjf.modules.core.bean.Query;
import org.bjf.modules.core.web.core.PageVO;
import org.bjf.utils.ExceptionAssert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 服务基类，封装了基本的方法
 *
 * @param <M> mybatis mapper
 * @param <T> entity
 * @param <Q> 查询条件
 * @author bjf
 * @desc 方法默认没有使用@Transactional标记事务, 如要使用事务，建议在自己的service里面控制
 * @date 2017/11/27
 */
@Slf4j
public abstract class BaseService<M extends BaseMapper<T>, T, Q extends Query>
        // 不继承mybatis-plus的实现
//        extends ServiceImpl<M, T>
{

    private static final int IN_SIZE = 1000;

    @Autowired
    protected M baseMapper;

    /**
     * 新增
     */
    public boolean add(T obj) {
        Integer affCnt = baseMapper.insert(obj);
        return affCnt > 0;
    }

    public void addBatch(Collection<T> list) {
        this.addBatch(list, 1000);
    }

    /**
     * 新增(批量)
     */
    @Transactional(rollbackFor = Exception.class)
    public void addBatch(Collection<T> list, int batchSize) {
        if (CollectionUtils.isEmpty(list)) {
            log.warn("批量插入的集合为空");
            return;
        }
        if (batchSize < 1) {
            batchSize = 1000;
        }

        Class tClz = ReflectionKit.getSuperClassGenericType(getClass(), this.getClass(), 1);
        SqlSessionFactory sqlSessionFactory = GlobalConfigUtils.currentSessionFactory(tClz);
        SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH, Boolean.FALSE);

        String sqlStatement = SqlHelper.getSqlStatement(tClz, SqlMethod.INSERT_ONE);
        try {
            int i = 0;
            for (T t : list) {
                batchSession.insert(sqlStatement, t);
                if (i >= 1 && i % batchSize == 0) {
                    batchSession.flushStatements();
                }
                i++;
            }
            batchSession.flushStatements();
        } finally {
            batchSession.close();
        }
    }

    /**
     * 修改
     */
    public boolean updateById(T obj) {
        return baseMapper.updateById(obj) > 0;
    }

    /**
     * 修改
     */
    public boolean update(T obj, Wrapper<T> qw) {
        return baseMapper.update(obj, qw) > 0;
    }

    /**
     * 主键删除
     */
    public boolean deleteById(Serializable id) {
        Integer affCnt = baseMapper.deleteById(id);
        return affCnt > 0;
    }

    /**
     * 主键删除
     */
    public void deleteByIds(Collection<? extends Serializable> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            log.warn("idList集合为空");
            return;
        }
        baseMapper.deleteBatchIds(idList);
    }

    /**
     * 删除
     */
    public boolean delete(Wrapper<T> qw) {
        Integer affCnt = baseMapper.delete(qw);
        return affCnt > 0;
    }

    /**
     * 删除  不提供Query参数的方法，字段为空的时候容易删除值全表
     */
 /* public boolean delete(Q query) {
    return this.delete(buildQuery(query));
  }*/

    /**
     * ID 取对象,取不到为空
     */
    public T get(Serializable id) {
        return baseMapper.selectById(id);
    }

    /**
     * 查询一条记录
     */
    public T getOne(Q query) {
        return getOne(buildQuery(query));
    }

    /**
     * 查询一条记录
     */
    public T getOne(Wrapper<T> qw) {

        Page<T> p = new Page<>(1, 2);
        // 不发起count查询
        p.setSearchCount(Boolean.FALSE);

        IPage<T> page = baseMapper.selectPage(p, qw);
        if (page.getRecords().isEmpty()) {
            return null;
        }
        int size = page.getRecords().size();
        if (size > 1) {
            log.warn("getOne查询到多条记录,只返回第一条");
        }
        return page.getRecords().get(0);
    }

    /**
     * 判断是否存在
     */
    public Boolean exist(Q query) {
        return exist(buildQuery(query));
    }

    /**
     * 判断是否存在
     */
    public Boolean exist(Wrapper<T> qw) {

        Page<T> p = new Page<>(1, 1);
        // 不发起count查询
        p.setSearchCount(Boolean.FALSE);

        IPage<T> page = baseMapper.selectPage(p, qw);
        if (page.getRecords().isEmpty()) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * ID 取对象,取不到对象抛异常
     */
    public T getRequired(Serializable id) {
        return this.getRequired(id, CommMsgCode.NO_DATA.getMessage());
    }

    /**
     * ID 取对象,取不到对象抛异常
     */
    public T getRequired(Serializable id, String errorMsg) {
        T obj = baseMapper.selectById(id);
        if (StringUtils.isBlank(errorMsg)) {
            ExceptionAssert.notNull(obj, CommMsgCode.NO_DATA);
        } else {
            ExceptionAssert.notNull(obj, CommMsgCode.NO_DATA, errorMsg);
        }

        return obj;
    }

    /**
     * count查询
     */
    public long count(Q query) {
        return count(this.buildQuery(query));
    }

    /**
     * count查询
     */
    public long count(Wrapper<T> qw) {
        return baseMapper.selectCount(qw);
    }

    /**
     * 只返回分页列表（不发起count查询）
     */
    public List<T> list(Q query) {
        return list(this.buildQuery(query), query.getPage(), query.getPageSize());
    }

    /**
     * 只返回分页列表（不发起count查询）
     */
    public List<T> list(Wrapper<T> qw, int page, int pageSize) {
        Page<T> p = new Page<>(page, pageSize);
        // 不发起count查询
        p.setSearchCount(Boolean.FALSE);

        return baseMapper.selectPage(p, qw).getRecords();
    }

    /**
     * 返回分页对象
     */
    public PageVO<T> listPage(Q query) {
        return listPage(this.buildQuery(query), query.getPage(), query.getPageSize());
    }

    /**
     * 返回分页对象
     */
    public PageVO<T> listPage(Wrapper<T> qw, int page, int pageSize) {
        Page<T> p = new Page<>(page, pageSize);
        IPage<T> ip = baseMapper.selectPage(p, qw);

        return new PageVO(ip.getRecords(), p);
    }

    /**
     * 查询所有
     */
    public List<T> listAll(Q query) {
        return listAll(this.buildQuery(query));
    }

    /**
     * 查询所有
     */
    public List<T> listAll(Wrapper<T> ew) {
        return baseMapper.selectList(ew);
    }

    /**
     * id 集合查询(返回List)
     */
    public List<T> listByIds(Collection<? extends Serializable> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            log.warn("ids集合为空");
            return Collections.emptyList();
        }
        if (ids.size() > IN_SIZE) {
            log.warn("ids集合大于" + IN_SIZE);
            return Collections.emptyList();
        }
        return baseMapper.selectBatchIds(ids);
    }

    /**
     * id 集合查询(返回Map)
     *
     * @param idClass 主键类型
     */
    public <ID> Map<ID, T> mapByIds(Collection<? extends Serializable> ids, Class<ID> idClass) {
        List<T> list = this.listByIds(ids);
        Map<ID, T> objMap = new HashMap<>(list.size());

        if (list.isEmpty()) {
            return objMap;
        }
        //=== 1.根据对象类型取主键字段
        TableInfo tableInfo = TableInfoHelper.getTableInfo(list.get(0).getClass());
        String idField = tableInfo.getKeyProperty();
        //=== 2.放到map里面减少遍历
        for (T obj : list) {
            try {
                ID idVal;
                String idValStr = BeanUtils.getProperty(obj, idField);
                if (Long.class.isAssignableFrom(idClass)) {
                    idVal = (ID) Long.valueOf(idValStr);
                } else if (Integer.class.isAssignableFrom(idClass)) {
                    idVal = (ID) Integer.valueOf(idValStr);
                } else {
                    idVal = (ID) idValStr;
                }
                objMap.put(idVal, obj);
            } catch (Exception e) {
                throw new ServiceException(CommMsgCode.SERVER_ERROR, e);
            }
        }
        return objMap;
    }

    /**
     * 暴露mapper
     */
    public M getMapper() {
        return this.baseMapper;
    }

    protected Wrapper<T> buildQuery(Q query) {
        return null;
    }


}
